using System;
using System.Collections.Generic;
using System.Text;
using GoodStuff.Text;
using System.Collections.Specialized;
using System.Configuration;
using System.Globalization;
using System.Net.Mail;
using System.Net.Mime;
using System.Text.RegularExpressions;

namespace GoodStuff.Providers
{
    /// <summary>
    /// To configure the messagingprovider, Configure the SmtpServer settings in web.config.
    /// </summary>
    /// <remarks>
    /// Requires the smtpMessaging configuration settings in the web.config
    /// </remarks>
    public class SmtpMessagingProvider : MessagingProviderBase
    {
        SmtpMessagingConfigurationSection config;

        public override void Initialize(string name, NameValueCollection configuration)
        {
            base.Initialize(name, configuration);

            config = (SmtpMessagingConfigurationSection)ConfigurationManager.GetSection("goodStuff/smtpMessaging");
            if (config == null)
            {
                throw new ArgumentNullException("SmtpMessagingProvider: goodStuff/smtpMessaging section required");
            }
        }

        /// <summary>
        /// Sends a message using a configured formatting template.
        /// </summary>
        /// <example>
        /// To send a Html Message with embedded images, just use 'img src=filename.jpg' in your template and put the JPG images
        /// in the same folder as the html-template. This of course only works when AsHtml = true
        /// </example>
        /// <param name="message"></param>
        public override void SendMessage(Message message)
        {
            if (string.IsNullOrEmpty(message.MessageClass))
            {
                throw new ArgumentNullException("SmtpMessagingProvider: MessageClass is required for each message specified.");
            }

            SmtpMessageConfigurationEntry entry = config.Messages[message.MessageClass];
            if(entry == null)
            {
                throw new IndexOutOfRangeException("SmtpMessagingProvider: No entry configured for '" + message.MessageClass + "'");
            }

            //make sure FROM, TO, SUBJECT are configured.

            //retrieve the message template from the web configuration.
            System.Net.Mail.MailMessage mailMessage = new System.Net.Mail.MailMessage();
            try
            {
                mailMessage.From = new System.Net.Mail.MailAddress(Templating.FormatCollection(entry.From, message));

                if (!string.IsNullOrEmpty(config.ForceRecipient))
                {
                    mailMessage.To.Add(config.ForceRecipient);
                }
                else
                {
                    mailMessage.To.Add(Templating.FormatCollection(entry.To, message));

                    string cc = Templating.FormatCollection(entry.CC, message);
                    string bcc = Templating.FormatCollection(entry.BCC, message);

                    if (!string.IsNullOrEmpty(cc))
                    {
                        mailMessage.CC.Add(cc);
                    }
                    if (!string.IsNullOrEmpty(bcc))
                    {
                        mailMessage.Bcc.Add(bcc);
                    }
                }

                mailMessage.Subject = Templating.FormatCollection(entry.Subject, message);
                mailMessage.IsBodyHtml = entry.AsHtml;

                //compose the body of the message by loading the template file and map the properties accordingly.
                mailMessage.Body = Templating.FormatFromFile(GetFullPath(entry.TemplateFile), CultureInfo.CurrentCulture, message);

                if (entry.AsHtml)
                {
                    //find all images, replace those images with embed ID's and add them as an attachment.
                    //for now, process <img> tags.

                    List<string> imagesFound = new List<string>();
                    List<LinkedResource> resources = new List<LinkedResource>();

                    Regex pattern = new Regex(@"(?<=img\s+src\=[\x27\x22])(?<Url>[^\x27\x22]*)(?=[\x27\x22])", RegexOptions.IgnoreCase);
                    foreach (Match match in pattern.Matches(mailMessage.Body))
                    {
                        if (!imagesFound.Contains(match.Groups["Url"].Value))
                        {
                            imagesFound.Add(match.Groups["Url"].Value);
                        }
                    }

                    // @"url\((?<url>.*?)\)
                    Regex pattern2 = new Regex(@"url\(\'(?<Url>.*?)\'\)", RegexOptions.IgnoreCase);
                    foreach (Match match in pattern2.Matches(mailMessage.Body))
                    {
                        if (!imagesFound.Contains(match.Groups["Url"].Value))
                        {
                            imagesFound.Add(match.Groups["Url"].Value);
                        }
                    }

                    string imagePath = System.IO.Path.GetDirectoryName(entry.TemplateFile);

                    foreach (string image in imagesFound)
                    {
                        string imageFile = GetFullPath(System.IO.Path.Combine(imagePath, image));
                        string imageName = image.Replace("/", "_");
                        if (System.IO.File.Exists(imageFile))
                        {
                            var newResource = new LinkedResource(imageFile, MediaTypeNames.Image.Jpeg)
                            {
                                ContentId = imageName,
                                TransferEncoding = TransferEncoding.Base64
                            };
                            // http://social.msdn.microsoft.com/Forums/en-AU/wlgeneraldev/thread/223bc74b-e0d1-4712-a8f8-c9c449ce5f61

                            switch (System.IO.Path.GetExtension(imageFile).ToLower())
                            {
                                case ".png":
                                    newResource.ContentType.MediaType = "image/png";
                                    break;
                                case ".jpg":
                                    newResource.ContentType.MediaType = "image/jpeg";
                                    break;
                                case ".gif":
                                    newResource.ContentType.MediaType = "image/gif";
                                    break;
                            }
                            newResource.ContentType.Name = imageName;

                            resources.Add(newResource);

                            //mutate old body.
                            mailMessage.Body = mailMessage.Body.Replace(image, "cid:" + imageName);
                        }
                    }

                    AlternateView body = AlternateView.CreateAlternateViewFromString(mailMessage.Body, null, MediaTypeNames.Text.Html);
                    foreach (var resource in resources)
                    {
                        body.LinkedResources.Add(resource);
                    }
                    mailMessage.AlternateViews.Add(body);
                    mailMessage.Body = null;
                }

                //Add attachments; 
                foreach (MessageAttachment attachment in message.Attachments)
                {
                    if (attachment is MessageAttachmentFromFile)
                    {
                        MessageAttachmentFromFile fileAttachment = attachment as MessageAttachmentFromFile;
                        if (string.IsNullOrEmpty(fileAttachment.MimeType))
                        {
                            mailMessage.Attachments.Add(new Attachment(fileAttachment.Filename));
                        }
                        else
                        {
                            mailMessage.Attachments.Add(new Attachment(fileAttachment.Filename, fileAttachment.MimeType));
                        }
                    }
                    else if (attachment is MessageAttachmentFromStream)
                    {
                        MessageAttachmentFromStream streamAttachment = attachment as MessageAttachmentFromStream;
                        mailMessage.Attachments.Add(new Attachment(streamAttachment.ContentStream, streamAttachment.Name, streamAttachment.MimeType));
                    }
                }

                SmtpClient client = new SmtpClient();
                client.Send(mailMessage);
            }
            catch (Exception ee)
            {
                throw new SmtpMessagingException("Unable to send message", message, mailMessage, ee);
            }
        }   
        
        private string GetFullPath(string filename)
        {
            //support for absolute paths.
            if (System.IO.Path.IsPathRooted(filename) == false)
            {
                if (string.IsNullOrEmpty(config.TemplateRoot))
                {
                    //detour for being compatible with hosting and local test purposes (and thus different path determination)
                    if (System.Web.Hosting.HostingEnvironment.IsHosted)
                    {
                        return System.IO.Path.Combine(System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, filename);
                    }
                    else
                    {
                        return System.IO.Path.Combine(System.Environment.CurrentDirectory, filename);
                    }
                }
                else
                {
                    return System.IO.Path.Combine(config.TemplateRoot, filename);
                }
            }

            return filename;
        }
    }
}
